/**
 * 哈希表。
 */
#include <cds/hash_table.h>

struct hlist_head *first_hlist_head_safe(struct hash_table *htab)
{
    /// 检查哈希表是否无效。
    if (htab == NULL || htab->nums == 0) {
        return NULL;
    }

    return htab->buckets;
}

struct hlist_head *next_hlist_head_safe(
    struct hash_table *htab,
    struct hlist_head *head
)
{
    /// 检查哈希表是否无效。
    if (htab == NULL || htab->nums == 0) {
        return NULL;
    }
    /// 如果当前哈希桶不在哈希表中，或为最后一个，返回NULL。
    if (head < htab->buckets || head >= (htab->buckets + htab->bucket_num)) {
        return NULL;
    }

    return head + 1;
}

struct hash_table *init_hash_table(
    struct hash_table *htab,
    struct hlist_head *buckets,
    unsigned long bucket_num,
    struct hash_table_opts *opts
)
{
    if (htab == NULL || buckets == NULL || bucket_num == 0 || opts == NULL) {
        return NULL;
    }
    if (opts->hash_func == NULL
        || opts->cmp_func == NULL
        || opts->get_key_func == NULL
    ) {
        return NULL;
    }

    htab->bucket_num = bucket_num;
    htab->buckets = buckets;
    htab->opts = opts;
    size_t buckets_size = sizeof(struct hlist_head*) * bucket_num;
    memset(buckets, 0, buckets_size);
    return htab;
}

void init_hash_node(struct hlist_node *hnode)
{
    if (hnode == NULL) {
        return;
    }
    hnode->next = NULL;
    hnode->pprev = NULL;
}

/**
 * @brief 删除一个哈希节点。
 * 
 * @param node 被删除的哈希节点。
 */
static inline void __hlist_del_node(struct hlist_node * node)
{
    struct hlist_node **pprev = node->pprev;
    struct hlist_node *next = node->next;
    *pprev = next;
    if (next != NULL) {
        next->pprev = pprev;
    }
}

/**
 * @brief 删除一个哈希节点，并将其初始化。
 * 
 * @param node 被删除的节点。
 */
static inline void __hlist_del_init(struct hlist_node * node)
{
    __hlist_del_node(node);
    init_hash_node(node);
}

/**
 * @brief 替换掉一个哈希节点。
 * 
 * @param old_node 被替换的哈希节点。
 * @param new_node 用于替换的哈希节点。
 */
static inline void __hlist_replace_node(
    struct hlist_node *old_node, 
    struct hlist_node *new_node
)
{
    *new_node = *old_node;
    *old_node->pprev = new_node;
    if (old_node->next != NULL) {
        old_node->next->pprev = &new_node->next;
    }
}

/**
 * @brief 将一个哈希节点插入到哈希桶的起始位置。
 * 
 * @param head 哈希桶。
 * @param node 被插入的哈希节点。
 */
static inline void __hlist_add_head(
    struct hlist_head *head,
    struct hlist_node *node
) 
{ 
    struct hlist_node *first = head->first; 
    node->next = first; 
    if (first != NULL) {
        first->pprev = &node->next; 
    }
    head->first = node;
    node->pprev = &head->first; 
}
/**
 * @brief 将一个哈希节点添加到另一个哈希节点之前。
 * 
 * @param pprev 现存哈希节点。
 * @param node 被插入的哈希节点。
 */
static inline void __hlist_add_before(
    struct hlist_node *next,
    struct hlist_node *node
)
{
    node->pprev = next->pprev; 
    node->next = next; 
    next->pprev = &node->next; 
    *(node->pprev) = node; 
}


/**
 * @brief 将一个哈希节点添加到另一个哈希节点之后。
 * 
 * @param pprev 现存哈希节点。
 * @param node 被插入的哈希节点。
 */
static inline void __hlist_add_node_after(
    struct hlist_node *prev,
    struct hlist_node *node
)
{
    node->next = prev->next; 
    prev->next = node;
    node->pprev = &prev->next; 
    if(node->next != NULL) {
        node->next->pprev = &node->next; 
    }
}

struct hlist_node *insert_hash_node(
    struct hash_table *htab,
    struct hlist_node *node,
    int replace
)
{
    if (htab == NULL || node == NULL || htab->bucket_num == 0) {
        return NULL;
    }
    unsigned long node_key = htab_get_key(htab, node);
    unsigned long hash = htab_hash(htab, node_key);
    unsigned long b_idx = hash % htab->bucket_num;

    /// 在对应的哈希桶中检查是否以及存在对应键值的哈希节点。
    struct hlist_node *target = first_hlist_node_safe(htab->buckets + b_idx);
    unsigned long target_key = 0;
    while (target != NULL) {
        target_key = htab_get_key(htab, target);
        if (htab_keys_cmp(htab, node_key, target_key) == 0) {
            break;
        }
        target = next_hlist_node_safe(target);
    }

    /// 进行插入操作。
    if (target == NULL) {
        target = node;
        __hlist_add_head(htab->buckets + b_idx, target);
        htab->nums += 1;
    } else if (replace) {
        __hlist_replace_node(target, node);
        init_hash_node(target);
    }
    return target;
}

struct hlist_node *find_key_hash_node(
    struct hash_table *htab,
    unsigned long key
)
{
    if (htab == NULL || htab->bucket_num == 0 || htab->bucket_num == 0) {
        return NULL;
    }
    unsigned long hash = htab_hash(htab, key);
    unsigned long b_idx = hash % htab->bucket_num;

    /// 在对应的哈希桶中检查是否以及存在对应键值的哈希节点。
    struct hlist_node *target = first_hlist_node_safe(htab->buckets + b_idx);
    unsigned long target_key = 0;
    while (target != NULL) {
        target_key = htab_get_key(htab, target);
        if (htab_keys_cmp(htab, key, target_key) == 0) {
            break;
        }
        target = next_hlist_node_safe(target);
    }

    return target;
}

struct hlist_node *del_hash_node(
    struct hash_table *htab,
    struct hlist_node *node
)
{
    if (htab == NULL || htab->bucket_num == 0 || htab->bucket_num == 0) {
        return NULL;
    }
    htab->nums -= 1;
    __hlist_del_init(node);
    return node;
}

struct hlist_node *del_key_hash_node(
    struct hash_table *htab,
    unsigned long key
)
{
    struct hlist_node *node = find_key_hash_node(htab, key);
    if (node == NULL) {
        return NULL;
    }
    return del_hash_node(htab, node);
}
